 package com.xsy.camera.demo

import android.Manifest
import android.annotation.SuppressLint
import android.content.ContentValues
import android.content.Context
import android.content.pm.PackageManager
import android.hardware.camera2.CameraCharacteristics
import android.hardware.camera2.CameraManager
import android.os.Build
import android.os.Bundle
import android.os.Environment
import android.provider.MediaStore
import android.util.Log
import android.view.GestureDetector
import android.view.MotionEvent
import android.widget.Toast
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.camera.core.CameraSelector
import androidx.camera.core.ExperimentalLensFacing
import androidx.camera.core.FocusMeteringAction
import androidx.camera.core.ImageCapture
import androidx.camera.core.ImageCaptureException
import androidx.camera.core.Preview
import androidx.camera.core.SurfaceOrientedMeteringPointFactory
import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.camera.video.FallbackStrategy
import androidx.camera.video.MediaStoreOutputOptions
import androidx.camera.video.Quality
import androidx.camera.video.QualitySelector
import androidx.camera.video.Recorder
import androidx.camera.video.Recording
import androidx.camera.video.VideoCapture
import androidx.camera.video.VideoRecordEvent
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.core.content.PermissionChecker
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import com.xsy.camera.demo.databinding.ActivityMainBinding
import java.text.SimpleDateFormat
import java.util.Locale
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
class MainActivity : AppCompatActivity() {
    private lateinit var viewBinding: ActivityMainBinding
    private var imageCapture: ImageCapture? = null
    private var videoCapture: VideoCapture<Recorder>?= null
    private var recording: Recording? = null

    private lateinit var cameraExecutor: ExecutorService
    private var cameraSelector: CameraSelector = CameraSelector.DEFAULT_FRONT_CAMERA
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        enableEdgeToEdge()
        viewBinding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(viewBinding.root)
        ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
            val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
            v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
            insets
        }
        viewBinding.imageCaptureBtn.setOnClickListener { takePhoto() }
        viewBinding.videoCaptureBtn.setOnClickListener { videoCapture() }
        viewBinding.switchCamera.setOnClickListener { switchCamera() }
        if (allPermissionGranted()) {
            startCamera()
        } else {
            ActivityCompat.requestPermissions(this, REQUEST_PERMISSIONS, REQUEST_CODE_PERMISSION)
        }
        cameraExecutor = Executors.newSingleThreadExecutor()
        cameraList()
    }

    private fun videoCapture() {
        val videoCapture = this.videoCapture ?: return

        viewBinding.videoCaptureBtn.isEnabled = false

        val curRecording = recording
        if (curRecording != null) {
            // Stop the current recording session.
            curRecording.stop()
            recording = null
            return
        }

        // create and start a new recording session
        val name = SimpleDateFormat(FILENAME_FORMAT, Locale.CHINA)
            .format(System.currentTimeMillis())
        val contentValues = ContentValues().apply {
            put(MediaStore.MediaColumns.DISPLAY_NAME, name)
            put(MediaStore.MediaColumns.MIME_TYPE, "video/mp4")
            if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) {
                put(MediaStore.Video.Media.RELATIVE_PATH, "Movies/CameraX-Video")
            }
        }

        val mediaStoreOutputOptions = MediaStoreOutputOptions
            .Builder(contentResolver, MediaStore.Video.Media.EXTERNAL_CONTENT_URI)
            .setContentValues(contentValues)
            .build()

        recording = videoCapture.output
            .prepareRecording(this, mediaStoreOutputOptions)
            .apply {
                if (PermissionChecker.checkSelfPermission(this@MainActivity,
                        Manifest.permission.RECORD_AUDIO) ==
                    PermissionChecker.PERMISSION_GRANTED)
                {
                    withAudioEnabled()
                }
            }
            .start(ContextCompat.getMainExecutor(this)) { recordEvent ->
                when(recordEvent) {
                    is VideoRecordEvent.Start -> {
                        viewBinding.videoCaptureBtn.apply {
                            text = getString(R.string.stop_capture)
                            isEnabled = true
                        }
                    }
                    is VideoRecordEvent.Finalize -> {
                        if (!recordEvent.hasError()) {
                            val msg = "Video capture succeeded: " +
                                    "${recordEvent.outputResults.outputUri}"
                            Toast.makeText(baseContext, msg, Toast.LENGTH_SHORT)
                                .show()
                            Log.d(TAG, msg)
                        } else {
                            recording?.close()
                            recording = null
                            Log.e(TAG, "Video capture ends with error: " +
                                    "${recordEvent.error}")
                        }
                        viewBinding.videoCaptureBtn.apply {
                            text = getString(R.string.start_capture)
                            isEnabled = true
                        }
                    }
                }
            }
    }

    private fun switchCamera() {
        cameraSelector = if (cameraSelector == CameraSelector.DEFAULT_BACK_CAMERA) {
            CameraSelector.DEFAULT_FRONT_CAMERA
        } else {
            CameraSelector.DEFAULT_BACK_CAMERA
        }
        startCamera()
    }

    override fun onRequestPermissionsResult(
        requestCode: Int,
        permissions: Array<out String>,
        grantResults: IntArray
    ) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        if (requestCode == REQUEST_CODE_PERMISSION) {
            if (allPermissionGranted()) {
                startCamera()
            } else {
                Toast.makeText(this, "Permission not granted by the user", Toast.LENGTH_SHORT)
                    .show()
                ActivityCompat.requestPermissions(
                    this,
                    REQUEST_PERMISSIONS,
                    REQUEST_CODE_PERMISSION
                )
            }
        }
    }

     private fun cameraList() {
         val cameraManager: CameraManager =
             getSystemService(Context.CAMERA_SERVICE) as CameraManager
         val cameraIdList: Array<String> =
             cameraManager.cameraIdList // 获取所有摄像头ID
         for (cameraId in cameraIdList) {
             val characteristics = cameraManager.getCameraCharacteristics(cameraId);
             val facing = characteristics.get(CameraCharacteristics.LENS_FACING);
             if (facing == CameraCharacteristics.LENS_FACING_BACK) {
                 Log.d("Camera", "后置摄像头ID: " + cameraId);
             } else if (facing == CameraCharacteristics.LENS_FACING_FRONT) {
                 Log.d("Camera", "前置摄像头ID: " + cameraId);
             }else{
                 Log.d("Camera", "摄像头ID: " + cameraId);
             }
         }


     }

    override fun onDestroy() {
        super.onDestroy()
        cameraExecutor.shutdown()
    }

    private fun allPermissionGranted() = REQUEST_PERMISSIONS.all {
        ContextCompat.checkSelfPermission(this, it) == PackageManager.PERMISSION_GRANTED
    }

    @SuppressLint("ClickableViewAccessibility")
    private fun startCamera() {
        val cameraProviderFuture = ProcessCameraProvider.getInstance(this)
        cameraProviderFuture.addListener({
            try {
                val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get()
                val preview = Preview.Builder().build().also {
                    it.setSurfaceProvider(viewBinding.viewFinder.surfaceProvider)
                }
                val recorder = Recorder.Builder()
                    .setQualitySelector(
                        QualitySelector.from(
                            Quality.HIGHEST,
                        FallbackStrategy.higherQualityOrLowerThan(Quality.SD)))
                    .build()
                videoCapture = VideoCapture.withOutput(recorder)


                imageCapture =
                    ImageCapture.Builder().setFlashMode(ImageCapture.FLASH_MODE_AUTO).build()

                cameraProvider.unbindAll()
                val camera =
                    cameraProvider.bindToLifecycle(
                        this,
                        cameraSelector,
                        preview,
                        imageCapture,
                        videoCapture
                    )
                val cameraInfo = camera.cameraInfo
                val cameraControl = camera.cameraControl
//                cameraControl.enableTorch(true)
                val meteringPointFactory = SurfaceOrientedMeteringPointFactory(
                    viewBinding.viewFinder.width.toFloat(),
                    viewBinding.viewFinder.height.toFloat()
                )
                val gestureDetector =
                    GestureDetector(this, object : GestureDetector.SimpleOnGestureListener() {
                        override fun onScroll(
                            e1: MotionEvent?,
                            e2: MotionEvent,
                            distanceX: Float,
                            distanceY: Float
                        ): Boolean {
                            val currentZoomRatio = cameraInfo.zoomState.value?.zoomRatio ?: 1f
                            val delta = (e1?.y ?: 0f) - e2.y
                            val scale = delta / viewBinding.viewFinder.height.toFloat()
                            val zoomRatio = currentZoomRatio + scale * ZOOM_SENSITIVITY
                            val maxZoomRatio = cameraInfo.zoomState.value?.maxZoomRatio ?: 1f
                            cameraControl.setZoomRatio(
                                zoomRatio.coerceIn(
                                    0f,
                                    maxZoomRatio
                                )
                            )
                            return true
                        }

                        override fun onSingleTapConfirmed(e: MotionEvent): Boolean {
                            e.let {
                                val meteringPint = meteringPointFactory.createPoint(it.x, it.y)
                                val action = FocusMeteringAction.Builder(meteringPint).build()
                                cameraControl.startFocusAndMetering(action)
                            }
                            return true
                        }
                    })
                viewBinding.viewFinder.setOnTouchListener { _, event ->
                    gestureDetector.onTouchEvent(event)
                    true
                }
            } catch (e: Exception) {
                Log.e(TAG, "Failed", e)
            }

        }, ContextCompat.getMainExecutor(this))

    }

    private fun takePhoto() {
        Log.d(TAG, "test.................")
        val name = SimpleDateFormat(
            FILENAME_FORMAT,
            Locale.CHINA
        ).format(System.currentTimeMillis())

        val contentValues = ContentValues().apply {
            put(MediaStore.MediaColumns.DISPLAY_NAME, name)
            put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg")
            if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) {
                put(MediaStore.MediaColumns.RELATIVE_PATH, "Pictures/CameraX-Image")
            } else {
                put(
                    MediaStore.MediaColumns.DATA,
                    Environment.getExternalStorageDirectory().path + "/" + Environment.DIRECTORY_DCIM + "/" + name
                )
            }
        }

        val outputOptions = ImageCapture.OutputFileOptions.Builder(
            contentResolver,
            MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
            contentValues
        ).build()

        imageCapture?.takePicture(outputOptions, ContextCompat.getMainExecutor(this), object :
            ImageCapture.OnImageSavedCallback {
            override fun onImageSaved(outputFileResults: ImageCapture.OutputFileResults) {
                val msg = "success save image to ${outputFileResults.savedUri}"
                Toast.makeText(applicationContext, msg, Toast.LENGTH_SHORT).show()
            }

            override fun onError(exception: ImageCaptureException) {
                Toast.makeText(applicationContext, "image save failed", Toast.LENGTH_SHORT).show()
            }

        })
    }

    companion object {
        private const val ZOOM_SENSITIVITY = 0.1f
        private const val TAG = "CameraxDemo"
        private const val FILENAME_FORMAT = "yyyy-MM-dd-HH-mm-ss-SSS"
        private const val REQUEST_CODE_PERMISSION = 0x1
        private val REQUEST_PERMISSIONS = mutableListOf(
            Manifest.permission.CAMERA,
            Manifest.permission.RECORD_AUDIO
        ).apply {
            if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) {
                add(Manifest.permission.WRITE_EXTERNAL_STORAGE)
            }
        }.toTypedArray()
    }
}

